package cn.zymall.seckill.service.impl;

import cn.zymall.common.model.SecKillSpuAndUser;
import cn.zymall.common.model.SeckillSku;
import cn.zymall.common.utils.amqp.AMQPField;
import cn.zymall.common.utils.amqp.AMQPUtil;
import cn.zymall.common.utils.id.IDUtil;
import cn.zymall.common.utils.redis.RedisFieldItem;
import cn.zymall.common.utils.redis.RedisLock;
import cn.zymall.common.utils.redis.RedisUtil;
import cn.zymall.common.utils.response.HttpResult;
import cn.zymall.seckill.service.SecKillBusinessService;
import com.google.gson.Gson;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.util.Date;

/**
 * @author xuxianda
 * @Description: TODO(这里用一句话描述作用)
 * @date 2020/8/24 19:51
 * @Copyright
 * @return
 **/
@Service
public class SecKillBusinessServiceImpl implements SecKillBusinessService {
    @Autowired
    RedisLock redisLock;

    @Autowired
    RedisUtil redisUtil;

    @Autowired
    AMQPUtil amqpUtil;

    private static Gson gson = new Gson();

    @Override
    public HttpResult orders(Integer userId, Integer secKillSkuId, Integer secKillSpuId) {
        RedisFieldItem redisFieldItem = new RedisFieldItem();
        redisFieldItem.setSecKillSpuKey(secKillSpuId.toString());
        redisFieldItem.setSecKillSkuKey(secKillSkuId.toString());
        /**
         * 请求进来，将这个商品编号锁上，如果进来拿不到锁，就让他在里面自旋三次，三次还拿不到就直接返回一个排队人数较多请重试
         * 根据请求生成requestId 这个requestId是唯一的
         */
        String requestId = IDUtil.generate12BitUUID();
        try {
            /**
             *  给商品规格编号后面加一个后缀，每次判断这个值有没有被上锁就好了
             */
            System.out.println(redisFieldItem.getSecKillSkuKey());
            if (redisLock.lock(redisFieldItem.getSecKillSkuKey() + "_lock", requestId)) {
                System.out.println("拿到锁了");
                SecKillSpuAndUser secKillSpuAndUser = new SecKillSpuAndUser();
                try {
                    /**
                     * 这个时候再去查redis有没有库存
                     */
                    Integer skuSum = 0;
                    try {
                        skuSum = (Integer) redisUtil.get(redisFieldItem.getSecKillSkuKey() + ".sum");
                    } catch (NumberFormatException e) {
                        e.printStackTrace();
                        System.out.println("库存储存的值有异常");
                    }
                    if (skuSum > 0) {
                        secKillSpuAndUser.setOrdersTime(new Date());
                        secKillSpuAndUser.setSecKillSkuId(secKillSkuId);
                        secKillSpuAndUser.setSecKillSpuId(secKillSpuId);
                        /**
                         * 生成订单编号
                         */
                        secKillSpuAndUser.setOrderId(IDUtil.generate12BitUUID());
                        /**
                         * 修改库存
                         */
                        skuSum = skuSum - 1;
                        redisUtil.set(redisFieldItem.getSecKillSkuKey() + ".sum", skuSum);
                        try {
                            /**
                             * 将消息交给消息队列 让秒杀商品的订单模块去处理队列中的消息
                             */
                            amqpUtil.sendMsg(AMQPField.SEC_KILL_ORDERS, gson.toJson(secKillSpuAndUser));
                            return new HttpResult().success(200, "成功抢到商品", secKillSpuAndUser);
                        } catch (Exception e) {
                            e.printStackTrace();
                            return new HttpResult().fail(204, "排队人数较多，请重试", null);
                        } finally {
                            /**
                             * 释放锁
                             */
                            redisLock.unlock(redisFieldItem.getSecKillSkuKey() + "_lock", requestId);
                        }
                    }
                    /**
                     * 没有库存了 直接返回
                     */
                    redisLock.unlock(redisFieldItem.getSecKillSkuKey() + "_lock", requestId);
                    return new HttpResult().fail(204, "商品已抢光", null);
                } catch (Exception e) {
                    e.printStackTrace();
                    return new HttpResult().fail(205, "排队人数较多，请重试", null);
                }
            } else {
                System.out.println("没有拿到锁");
                return new HttpResult().fail(205, "排队人数较多，请重试", null);
            }
        } catch (Exception e) {
            e.printStackTrace();
            /**
             * 释放锁
             */
            redisLock.unlock(redisFieldItem.getSecKillSkuKey() + "_lock", requestId);

            /**
             * 这里可以发送短信 提醒管理员 系统出现异常
             */
            return new HttpResult().fail(205, "排队人数较多，请重试", null);
        }
    }
}
